
/**
 * 实现大文件分片上传、续传、秒传功能
 * @author yweijian
 * @date 2021-01-06
 * @version 1.0.0
 * @example
 * 
 * Easyfile.init({url : ctx + "easyfile/upload", business : "picture"});
 * Easyfile.init({url : ctx + "easyfile/upload", allowConfig : {suffix : true, suffixs : "png, jpg, jpeg"}});
 * 
 * 单个文件上传
 * Easyfile.upload(file, function(result){
 * 		console.info(result);
 * 		//Upload succeeded. Something to do !
 * }, function(err){
 * 		console.info(err);
 * 		//Upload failed. Something to do !
 * });
 * 
 * 多个文件上传，使用打开upload.html的上传界面
 * $.modal.openOptions({
 * 	title: "上传文件",
 *		url: prefix + "/upload/picture",
 * 		yes: function(index, layero){
 *          layer.close(index);
 * 			var iframeWin = layero.find('iframe')[0];
 * 			//文件上传成功结果集合信息
 *      	var data = iframeWin.contentWindow.Easyfile.results;
 *          console.info(data);
 * 			//Upload succeeded. Something to do !
 * 		}
 * });
 * 
 */

var Easyfile = {
	id : "form-easyfile-upload",
	//文件上传请求地址
	url : "",
	//版本号
	version : "1.0.0",
	//文件上传业务
	business: "",
	//文件存储
	files : [],
	//文件上传成功结果集合信息
	results : [],
	//分片，默认大小为5M
	chunkSize : 5 * 1024 * 1024,
	//最大分片数，即单个文件大小最大5GB
	maxChunk : 1024,
	//最大上传队列
	maxQueue : 10,
	//允许上传文件配置
	allowConfig : {
		//文件后缀扩展名校验
		suffix : true,
		//允许文件后缀扩展名
		suffixs : "png, jpg, jpeg, gif, bmp, svg, xls, xlsx, csv, ppt, pptx, doc, docx, pdf, txt, wps, avi, mp4, mov, rmvb, 3gp, asf, ogg, mp3, rmvb, wav, wma, wmv",
		//文件媒体类型校验
		mime : false,
		//允许媒体类型
		mimes : "image/png, image/jpeg, image/gif, image/bmp, image/svg+xml, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/pdf, text/plain, application/vnd.ms-works, video/x-msvideo, video/mp4, video/quicktime, audio/x-pn-realaudio, video/3gpp, video/x-ms-asf, audio/ogg, audio/x-mpeg, audio/x-pn-realaudio, audio/x-wav, audio/x-ms-wma, audio/x-ms-wmv"
	},
	/**
	 * 初始文件上传列表表格
	 * @example
	 * Easyfile.init({url : ctx + "easyfile/upload", business : "picture"});
     * Easyfile.init({url : ctx + "easyfile/upload", allowConfig : {suffix : true, suffixs : "png, jpg, jpeg"}});
	 */
	init : function(_options) {
		if (_options) {
			
			$.extend(true, Easyfile, _options);
			
			if(_options.business){
				var config = {
        	        url: Easyfile.url + "/" + Easyfile.business,
        	        type: "get",
        	        dataType: "json",
        	        beforeSend: function () {
        	        	$.modal.loading("正在处理中，请稍后...");
        	        },
        	        success: function(result) {
        	        	if (result.code == 0) {
        	        		Easyfile.allowConfig = result.data ? result.data : Easyfile.allowConfig;
        	        		Easyfile.allowableFileCheck();
        	        	}
        	        	$.modal.closeLoading();
        	        }
        	    };
        	    $.ajax(config);
			}else{
				Easyfile.allowableFileCheck();
			}
		}
		
		if($("#" + Easyfile.id).length > 0){
			var _data = Easyfile.files ? Easyfile.files : [];
			var options = {
				data : _data,
				pagination : false,
				showSearch : false,
				showColumns : false,
				showRefresh : false,
				showToggle : false,
				showExport : false,
				formatNoMatches : function() {
					return "请即将上传的文件添加到此处";
				},
				modalName : "文件",
				columns : [ {
					checkbox : true
				},
					{
						field : 'id',
						visible : false
					},
					{
						field : 'name',
						title : '名称',
						formatter : function(value, row, index) {
							return $.table.tooltip(value);
						}
					},
					{
						field : 'size',
						title : '大小',
						formatter : function(value, row, index) {
							return Easyfile.formatSize(value, 2);
						}
					},
					{
						field : 'status',
						title : '状态',
						formatter : function(value, row, index) {
							if (value == "loading") {
								return "加载中";
							} else if (value == "ready") {
								return "待上传";
							} else if (value == "starting") {
								return "上传中";
							} else if (value == "pausing") {
								return "暂停";
							} else if (value == "success") {
								return "成功";
							} else if (value == "error") {
								return "失败";
							} else {
								return "未知异常";
							}
						}
					},
					{
						field : 'progress',
						title : '进度',
						formatter : function(value, row, index) {
							return value || "0%";
						}
					},
					{
						title : '操作',
						align : 'center',
						formatter : function(value, row, index) {
							return $('#operateTpl').tmpl(row).html();
						}
					} ]
			};
			$.table.init(options);
		}
	},
	/**
	 * 添加上传文件
	 */
	add : function() {
		$('#' + Easyfile.id + ' input[type="file"]').click();
		$('#' + Easyfile.id + ' input[type="file"]').change(function(e) {
			var files = e.currentTarget.files;
			for (var k = 0; k < files.length; k++) {
				files[k].status = "loading"; //文件加载解析中
				if (Easyfile.allowableFileCheck(files[k])) {
					Easyfile.cuteFile(files[k]); //文件分片
				} else {
					break;
				}
			}
			Easyfile.reset();
		});
	},
	/**
	 * 开始上传
	 */
	start : function(id, success, error) {
		if (id) {
			var index = Easyfile.getFileIndexById(id);
			if (Easyfile.files[index].status != "starting" && Easyfile.files[index].status != "success") {
				Easyfile.files[index].status = "starting"; //开始上传
				upload(Easyfile.files[index], 0, success, error);
				Easyfile.refresh();
			}
		} else {
			var ids = Easyfile.getFileTableSelect();
			if (ids) {
				if (ids.length > Easyfile.maxQueue) {
					$.modal.msgWarning("最多能同时上传" + Easyfile.maxQueue + "个文件");
				}
				var queue = ids.length > Easyfile.maxQueue ? Easyfile.maxQueue : ids.length;
				for (var i = 0; i < queue; i++) {
					var index = Easyfile.getFileIndexById(ids[i]);
					if (Easyfile.files[index].status != "starting" && Easyfile.files[index].status != "success") {
						Easyfile.files[index].status = "starting"; //开始上传
						upload(Easyfile.files[index], 0, success, error);
					}
				}
				Easyfile.refresh();
			}
		}

		//分片上传
		function upload(file, index, success, error) {
			if (file.status != "starting") {
				Easyfile.refresh();
				return;
			}
			var blocks = file.blocks;
			if (!blocks || blocks.length <= 0 || index >= blocks.length) {
				Easyfile.refresh();
				return;
			}
			new Promise(function(resolve, reject) {
				if (blocks[index].id) {
					resolve({
						code : 0,
						data : blocks[index].id,
						msg : "读取二进制字符串成功"
					});
				} else {
					var reader = new FileReader();
					reader.readAsBinaryString(blocks[index].blob);
					reader.onload = function(event) {
						var md5 = $.md5(event.target.result);
						resolve({
							code : 0,
							data : md5,
							msg : "读取二进制字符串成功"
						});
					}
				}
			}).then(function(res) {
				new Promise(function(resolve, reject) {
					//加载中、暂停、错误、成功状态则停止上传
					if (file.status == undefined || file.status == "" || file.status == "loading" || file.status == "pausing" || file.status == "error" || file.status == "success") {
						Easyfile.refresh();
						return new Promise();
					} else {
						var fileIndex = Easyfile.getFileIndexById(file.id);
						blocks[index].id = res.data;
						Easyfile.files[fileIndex].blocks[index] = blocks[index];
						uploadChunk(blocks[index], resolve, reject);
					}
				}).then(function(res) {
					if (res && res.code == 0) { //分片上传成功
						if (res.data && res.data.id && res.data.status && res.data.status == "0") {
							index = blocks.length - 1;
							finishCallbak(file.id, blocks.length, index, "success");
							//分片全部上传结束
							Easyfile.results.push(res.data);
							//自定义成功回调函数
							if(typeof success == "function"){
								success(res);
							}
						} else {
							index = res.data.chunk; //当前分片
							finishCallbak(file.id, res.data.chunks, index);
							index++;
							upload(file, index); //继续轮询上传下一个分片
						}
					} else {
						finishCallbak(file.id, blocks.length, index, "error");
						//自定义失败回调函数
						if(typeof error == "function"){
							error(res);
						}
					}
				}, function(err) { //分片上传失败
					finishCallbak(file.id, blocks.length, index, "error");
					//自定义失败回调函数
					if(typeof error == "function"){
						error(err);
					}
				});
			});
		}
		
		/**
		 * 上传文件分片
		 */
		function uploadChunk(block, resolve, reject) {

			var formData = new FormData();
			//分片信息
			formData.append('id', block.id);
			formData.append('chunk', block.chunk);
			formData.append('chunks', block.chunks);
			formData.append('start', block.start);
			formData.append('end', block.end);
			formData.append('file', block.blob);
			//文件信息
			formData.append('fileId', block.file.id);
			formData.append('fileName', block.file.name);
			formData.append('fileSize', block.file.size);
			formData.append('fileType', block.file.type);

			$.ajax({
				url : Easyfile.url,
				data : formData,
				type : 'post',
				processData : false, // 不处理数据
				contentType : false, // 不设置内容类型
				success : function(result) {
					if (typeof resolve == 'function') {
						resolve(result);
					}
				},
				error : function(result) {
					if (typeof reject == 'function') {
						reject(result);
					}
				}
			});
		}
		
		//文件上传进度
		function finishCallbak(id, length, index, status) {
			var fileIndex = Easyfile.getFileIndexById(id);
			var progress = (((1 / parseFloat(length)) * (parseInt(index) + 1)) * 100).toFixed(2) + "%";
			Easyfile.files[fileIndex].progress = progress;
			if (status) {
				Easyfile.files[fileIndex].status = status;
			}
			Easyfile.refresh();
		}
		
	},
	/**
	 * 暂停上传
	 */
	pause : function(id) {
		if (id) {
			var index = Easyfile.getFileIndexById(id);
			Easyfile.files[index].status = "pausing"; //暂停上传
			Easyfile.refresh();
		} else {
			var ids = Easyfile.getFileTableSelect();
			if (ids) {
				for (var i = 0; i < ids.length; i++) {
					var index = Easyfile.getFileIndexById(ids[i]);
					Easyfile.files[index].status = "pausing"; //暂停上传
				}
				Easyfile.refresh();
			}
		}
	},
	/**
	 * 移除上传文件
	 * id为空移除表格选中项，不为空则移除该项
	 */
	remove : function(id) {
		if (id) {
			var index = Easyfile.getFileIndexById(id);
			$.modal.confirm("确定移除【" + Easyfile.files[index].name + "】文件吗？", function() {
				Easyfile.files.splice(index, 1);
				Easyfile.refresh();
			});
		} else {
			var ids = Easyfile.getFileTableSelect();
			if (ids) {
				$.modal.confirm("确定移除选中的" + ids.length + "个文件吗？", function() {
					for (var i = 0; i < ids.length; i++) {
						var index = Easyfile.getFileIndexById(ids[i]);
						Easyfile.files.splice(index, 1);
					}
					Easyfile.refresh();
				});
			}
		}
	},
	/**
	 * 单个文件分片上传，不建议使用超大文件
	 * @param {file} file 文件实体
	 * @param {success} success 上传成功回调函数
	 * @param {error} error 上传失败回调函数
	 */
	upload : function(file, success, error) {
		file.status = "loading"; //文件加载解析中
		if (Easyfile.allowableFileCheck(file)) {
			Easyfile.cuteFile(file); //文件分片
		}
		setTimeout(function() {
			Easyfile.start(file.id, success, error);
		}, 800);
	},
	/**
	 * 刷新表格
	 */
	refresh : function() {
		if($("#" + Easyfile.id).length > 0){
			$.table.destroy();
			Easyfile.init();
			$('[data-toggle="tooltip"]').tooltip();
		}
	},
	/**
	 * 重置fileinput
	 */
	reset : function() {
		if($("#" + Easyfile.id).length > 0){
			document.getElementById(Easyfile.id).reset();
		}
	},
	/**
	 * 负责将文件切片
	 */
	cuteFile : function(file) {
		var pending = [],
			total = file.size,
			chunks = Easyfile.chunkSize ? Math.ceil(total / Easyfile.chunkSize) : 1,
			index = 0,
			start = 0,
			len,
			end,
			blob;

		if (chunks > Easyfile.maxChunk) {
			$.modal.msgWarning("单个文件最大限制不超过" + Easyfile.formatSize(Easyfile.maxChunk * Easyfile.chunkSize));
			Easyfile.refresh();
			Easyfile.reset();
			return;
		}

		new Promise(function(resolve, reject) {
			var blob = file.slice(0, Easyfile.chunkSize);
			var reader = new FileReader();
			reader.readAsBinaryString(blob);
			reader.onload = function(event) {
				var md5 = $.md5(event.target.result);
				resolve({
					code : 0,
					data : md5,
					msg : "读取二进制字符串成功"
				});
			}
		}).then(function(res) {
			file.id = $.md5(res.data);
			var isExist = Easyfile.getFileIndexById(file.id);
			if (isExist == -1) {
				while (index < chunks) {
					len = Math.min(Easyfile.chunkSize, total - start);
					end = Easyfile.chunkSize ? (start + len) : total;
					blob = file.slice(start, end);
					pending.push({
						file : file,
						blob : blob,
						start : start,
						end : end,
						total : total,
						chunks : chunks,
						chunk : index++
					});
					start += len;
				}
				file.blocks = pending.concat();
				file.status = "ready";
				Easyfile.files.unshift(file);
				Easyfile.refresh();
				Easyfile.reset();
			}
		});
	},
	/**
	 * 根据文件id或者文件的索引号
	 */
	getFileIndexById : function(id) {
		if (id) {
			var j = 0;
			for (; j < Easyfile.files.length; j++) {
				if (id == Easyfile.files[j].id) {
					return j;
				}
			}
		}
		return -1;
	},
	/**
	 * 获取选中的文件
	 */
	getFileTableSelect : function() {
		var rows = $.common.isEmpty($.table._option.uniqueId) ? $.table.selectFirstColumns() : $.table.selectColumns($.table._option.uniqueId);
		if (rows.length == 0) {
			$.modal.alertWarning("请至少选择一条记录");
			return;
		}
		return rows;
	},
	/**
	 * 允许上传文件检验
	 */
	allowableFileCheck : function(file) {
		
		if(Easyfile.allowConfig.suffix == undefined && Easyfile.allowConfig.mime == undefined){
			$.modal.alertWarning("文件上传参数配置错误");
			return false;
		}
		if(Easyfile.allowConfig.suffix == false && Easyfile.allowConfig.mime == false){
			$.modal.alertWarning("文件上传参数配置错误");
			return false;
		}
		if(Easyfile.allowConfig.suffix == true && !Easyfile.allowConfig.suffixs){
			$.modal.alertWarning("文件上传参数配置错误");
			return false;
		}
		if(Easyfile.allowConfig.mime == true && !Easyfile.allowConfig.mimes){
			$.modal.alertWarning("文件上传参数配置错误");
			return false;
		}
		
		if(!file){
			return false;
		}
		
		if (Easyfile.allowConfig.suffix && Easyfile.allowConfig.suffix == true) {
			var suffix = file.name.substring(file.name.lastIndexOf(".") + 1, file.name.length);
			if (Easyfile.allowConfig.suffixs && Easyfile.allowConfig.suffixs.indexOf(suffix) == -1) {
				$.modal.alertWarning("请选择正确的文件格式：" + Easyfile.allowConfig.suffixs);
				return false;
			}
		}
		if (Easyfile.allowConfig.mime && Easyfile.allowConfig.mime == true) {
			var mime = file.type;
			if (Easyfile.allowConfig.mimes && Easyfile.allowConfig.mimes.indexOf(mime) == -1) {
				$.modal.alertWarning("请选择正确的媒体类型：" + Easyfile.allowConfig.mimes);
				return false;
			}
		}
		return true;
	},
	/**
     * 格式化文件大小, 输出成带单位的字符串
     * @method formatSize
     * @param {Number} size 文件大小
     * @param {Number} [pointLength=2] 精确到的小数点数。
     * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.
     * @example
     * console.log( Base.formatSize( 100 ) );    // => 100B
     * console.log( Base.formatSize( 1024 ) );    // => 1.00K
     * console.log( Base.formatSize( 1024, 0 ) );    // => 1K
     * console.log( Base.formatSize( 1024 * 1024 ) );    // => 1.00M
     * console.log( Base.formatSize( 1024 * 1024 * 1024 ) );    // => 1.00G
     * console.log( Base.formatSize( 1024 * 1024 * 1024, 0, ['B', 'KB', 'MB'] ) );    // => 1024MB
     */
	formatSize : function(size, pointLength, units) {
		var unit;
		units = units || [ 'B', 'K', 'M', 'G', 'TB' ];
		while ((unit = units.shift()) && size > 1024) {
			size = size / 1024;
		}
		return (unit === 'B' ? size : size.toFixed(pointLength || 2)) + unit;
	}
}